/* ---------------------------------------------------------------- HTTP -- */
import $fn from './fn'
import $wx from './wx'
import $config from '../../config'
/* ----------- api ----------- */
const api = $config.api[ $config.env ];
/* ----------- 序列化对象 ----------- */
/**
 *  序列化参数 {a:1,b:2} to ?a=1&b=2
 *
 * */
const serializeParam = (body,allowEmpty, ask) => {
	if(!$fn.hasObject(body)) return ''
	let param = body
	let str=''
	for(var i in param){
		if(allowEmpty){
			str += i + '=' + param[i] + '&'
		}else{
			if($fn.isNotEmpty(param[i])){ str += i + '=' + param[i] + '&' }
		}
	}
	if (str.charAt(str.length - 1) === '&'){ 
		str = str.slice(0, str.length - 1)
	}
	if(str){
		str = (ask===0 ? '' : '?') + str
	}else{
		str = encodeURI(str) 	// encodeURI 不对 [:, /, ;,?] 进行编码
	}
	return str
}
/**
 * 将 body 以函数形式处理
 *
 * */
const manageBody = (body,allowEmpty) => {
	if($fn.isFunction(body)){
		body = body()
	}else if($fn.hasObject(body)){
		for(let i in body){
			let v = body[i]
			if( $fn.isEmpty(v) ){
				if(allowEmpty === true){
					body[i] = ''
				}else if(allowEmpty === null){
					body[i] = null
				}else{
					delete body[i]
				}
			}
		}
	}
	return body
}
/* ----------- 跳转登录 ----------- */
const login = ()=>{
    wx.removeStorage({ key: 'user' });
    const pages = getCurrentPages();
    wx.setStorageSync('loginPage', '/' + pages[ pages.length - 1 ].route);
    setTimeout(() => {
        wx.redirectTo({ url: '/pages/login/index' });
    }, 1500)
}
/* ----------- 测试打印 ----------- */
const LogInfo = (method, api, param, data, index)=>{
	if($config.env){
		const colors = ['red', 'green', 'deepOrange']
		console.log(`%c ==================================【 ${method} 】【 ${api} 】==================================`, 'color:' + colors[index])
		console.log(' 参数：', param)
		console.log(' 数据：', data)
		console.log(`%c ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— end `, 'color:' + colors[index])
	}
}
const winHeight = wx.getSystemInfoSync().screenHeight
let run = 0;
const Index = {
    http(_this,url, param, opt){
        let tokenStr = $wx.getToken('token')
        let action = opt.action || 'POST'
        let header =  opt?.token === false ? {} : { Authorization: tokenStr }
        let data = {}
        const taskApi = url
        url =  api + url
        let empty = action === 'GET' ? false : true
        if(opt?.empty !== undefined){ empty = opt?.empty }
        // 自定义参数字段
        if(opt.setParam){ 
            if($fn.hasObject(param)){
                param = { ...param, ...setParam(param) }
            }
        } 		
        if(action === 'GET'){
            url =  url + serializeParam(param, empty)
        }else{
            data = manageBody(param, empty)
        }
        if(!$fn.isObject(_this?.task) && _this) _this.task = {}
        return new Promise((resolve, reject) => {
            const task = wx.request({
                header: { ...header },
                method:action,
                url,
                data,
                timeout: 60000,
                success: res => {   
                    if (res.statusCode == 200){
                        let resData = res.data;
                        const {code, data} = resData
                        if (code === 200) {
                            resolve(data)
                            LogInfo(action, url, param, data, 1)
                        } else if (code === $config.noLoginCode) {
                            this.abort(_this)
                            if($config.wxLogin) {
                                run ++
                                if(  run < 2 )  {
                                    opt?.onNotLogin()
                                    wx.hideNavigationBarLoading()
                                }
                            }else{
                                $wx.msg.info('请先登录')
                                login()
                            }
                            opt?.onError?.(resData)
                            LogInfo(action, url, param, resData, 2)
                        } else if (code === $config.loginExpireCode) {
                            this.abort(_this)
                            if($config.wxLogin) {
                                run ++
                                if(run < 2) {
                                    opt?.onTokenExpire?.()
                                    wx.hideNavigationBarLoading()
                                }
                            }else{
                                $wx.msg.info('登录过期，重新登录')
                                login()
                            }
                            opt?.onError?.(resData)
                            LogInfo(action, url, param, resData, 2)
                        }else if(code === 204){ // 用户不存在
                            if($config.wxLogin){
                                $wx.localRemove('user')
                                wx.showToast({ title: '用户不存在', icon: 'none' })
                                wx.restartMiniProgram({ path:'/pages/tabs/home/index' })
                            }
                            opt?.onError?.(resData)
                            LogInfo(action, url, param, resData, 2)
                        } else if(code===205){
                            $wx.msg.info('系统出错')
                            opt?.onError?.(resData)
                            LogInfo(action, url, param, '无', 0)
                        }else {
                            $wx.msg.info(resData['msg'])
                            opt?.onError?.(resData)
                            opt?.onFail?.(resData)        // 数据处理不满足条件时调用
                            reject(resData)
                            LogInfo(action, url, param, resData, 2)
                        }
                    }else{
                        opt?.onError?.();
                        $wx.msg.info('服务请求出错')
                        LogInfo(action, url, param, '无', 0)
                    }
                    if($config.wxLogin) run = 0               
                },
                fail({ errMsg }) {
                    run = 0
                   if(errMsg === 'request:fail abort') return $wx.msg.info('服务器无响应')
                    wx.getNetworkType({
                        success(res) {
                            wx.stopPullDownRefresh()
                            wx.hideNavigationBarLoading()
                            $wx.msg.info(res.networkType === 'none' ? '无网络' : '服务器无响应')
                        }
                    })
                    opt?.onError?.()
                },
                complete(res) {
                    opt?.onEnd?.(res)
                }
            })
            if(_this) _this.task[taskApi] = task
        })
    },
    post(_this, api, option){
        if( !api ) { Promise.reject(); return $wx.msg.error('接口未传递') }
        let opt = {
            param: {},
            loadingText:'数据提交中',        // 加载提示文字
            succeedText: '',				// 自定义成功提示
            succeedFn: '',					// 成功之后执行
            errorText: '',					// 自定义错误提示
            submitLoading: 'submitLoading', // 加载判断
            replace: null,					// replace 路由
            push: null,						// push 路由
            reLaunch:null,                   // 跳转重载
            back: false,					// 是否后退
            refresh:false,                  // 刷新
            closeToast: false,				// 是否关闭默认提示
            anywayHandler: () => { },		// 无论请求成功或失败都执行此方法
            upload: false,					// 调用上传接口
            runFirst: false,				// 先跳转，后提示
            loading:true,                   //
            barLoading:false,
            successToast:'',
            ...option
        }
        let param = opt.param
        if(_this){
           param = { ..._this.data.model, ...param}
            _this.setData({ [opt.submitLoading]: true })
        }
        opt.loading && wx.showLoading({ title: opt.loadingText + '...'})
        opt.barLoading && wx.showNavigationBarLoading()

        const run = () => {
            setTimeout(()=>{
                opt.tab && wx.switchTab({ url: opt.tab });
                opt.replace && wx.redirectTo({ url: opt.replace });
                opt.push && wx.navigateTo({ url: opt.push });
                opt.reLaunch && wx.reLaunch({ url: opt.reLaunch });
                opt.back && wx.navigateBack()
                opt.refresh && _this.onRefresh()
                opt.succeedFn && opt.succeedFn()
            },300)
        }
        if(opt.setParam){
            param = { ...param, ...opt.setParam(param) }
        }
        return new Promise((resolve,reject) => {
            this.http(_this, api, param, {
                onEnd: () => { 
                    _this?.setData?.({ [opt.submitLoading]: false }); 
                    opt.onEnd && opt.onEnd(); 
                    opt.loading && wx.hideLoading();
                    opt.barLoading && wx.hideNavigationBarLoading();
                },
                upload: opt.upload,
                okToast: opt.okToast,
                empty: opt.empty,
                setParam: opt.setParam,
                onNotLogin: () => {
                    $wx.login({ ok: () => this.post(_this, api, option) })
                },
                onTokenExpire: () => {
                    $wx.login({ ok: () => this.post(_this, api, option) })
                },
            }).then(data => {
                opt?.onOk?.(data)
                resolve(data)
                // 提示后执行
                if (opt.okText) {
                    $wx.msg.ok(opt.okText)
                }
                run()
            }, data => {
                reject(data)
            })
        })
    },
    pull(_this, api, option){
        if( !api ) { Promise.reject(); return $wx.msg.error('接口未传递') }
        let opt = {
            dataName: 'data',				// 数据名字
            param: {},						// 参数
            pullLoading: 'pullLoading',		// 加载判断
            loadingText: '加载中',
            onError: () => { },
            loading:false,
            format:[],
            formatSplit:0,
            barLoading: false,
            cutTitle:null,
            toArray:[],
            ...option
        }
     
        if(!opt.paging) {
            _this?.setData?.({ [opt.pullLoading]: true, refreshLoading:true })
            opt.loading && wx.showLoading({ title: opt.loadingText + '...' })
            opt.barLoading && wx.showNavigationBarLoading()
        }
        return new Promise((resolve, reject) => {
            let id = opt.id ? '/' + opt.id : ''
            this.http(_this, api + id, opt.param, {
                onError: () => {
                    opt?.onError?.()
                    opt.barLoading && wx.hideNavigationBarLoading()
                    wx.stopPullDownRefresh()
                },
                onEnd: opt?.onEnd ? opt?.onEnd :  () => {
                   _this?.setData?.({ [opt.pullLoading] : false, refreshLoading: false })
                    opt.loading && wx.hideLoading()
                },
                action:'GET',
                empty: opt.empty,
                setParam: opt.setParam,
                onNotLogin: opt?.onNoLogin ? opt?.onNoLogin :  () => {
                    $wx.login({ ok: () => opt?.auth ? _this.auth() : this.pull(_this, api, option) })
                },
                onTokenExpire: opt?.onTokenExpire ? opt?.onTokenExpire : () => {
                    $wx.login({ ok: () => opt?.auth ? _this.auth() : this.pull(_this, api, option) })
                },
            }).then(data => {
                opt?.onOk?.(data)
                if( !opt?.paging ){
                    if(opt.setName){ 
                        if($fn.hasArray(data)){
                            data = data.map( (v,i) => ({ ...v, ...opt.setName(v, i) }) )
                        }else if($fn.hasObject(data)){
                            data = { ...result, ...opt.setName(result) }
                        }else{
                            data = opt.setName(data)
                        }
                     } 		
                    _this?.setData?.({ [opt.dataName]: data })
                    resolve(data)
                }else{
                    resolve(data)
                }
            }, data => {
                reject(data)
            })
        })
    },
    paging(_this, api, option){
        if( !api ) { Promise.reject(); return $wx.msg.error('接口未传递') }
        let opt = {
            dataName: 'data',				        // 数据名字
            param: {},						        // 参数
            pageIndex:'pageIndex',                  // 当前页
            pagingLoading: 'pagingLoading',		    // 加载判断
            loadingComplete: 'loadingComplete',     // 加载完毕
            pager:'pager',                          // 
            loading: false,
            format:[],
            id: '#paging',
            paging: 'paging',
            loadingText: '数据加载中',
            ...option
        }
        let { param = {}, reset, refresh, pageIndex, pagingLoading, loadingComplete } = opt

        if( !_this[pageIndex] ) _this[pageIndex] = 1

        return new Promise((resolve,reject)=>{
            if( refresh || reset )  {
                _this.setData({ [loadingComplete]: false, refreshLoading: true })
                _this.task?.[api]?.abort?.()
            }else{
                if (_this.data[loadingComplete]) { return }
                if( _this[pageIndex + 'last'] === _this[pageIndex] ) {
                    // return wx.showToast({ title: '网络太慢，操作过快', icon: 'none' })
                    return
                }
            }
            _this[pageIndex + 'last'] = _this[pageIndex]
            
            if (_this.data[loadingComplete]) { return }
    
            _this.setData({ [pagingLoading]: true, [loadingComplete]: false })
    
            const {dataName = 'data', pageSizeName='size', pageNumName='page', totalItemsName='total_items', totalPagesName='total_pages', pageSize = 10  } = $config?.paging || {}
    
            if(opt.loading && _this.data[dataName].length === 0)  {
                wx.showLoading({ title: opt.loadingText + '...' })
            }
            let query =  opt?.query || _this.query
            if(reset){
                refresh = true
                _this.model = query
            }else{
                _this.model = { ...query, ..._this.model, ...param }
            }
            if( refresh == true || !_this[pageIndex] ){
                _this[pageIndex] = 1
            }
            const baseParam = {
                page: _this[pageIndex],
                size: pageSize
            }
            baseParam[ pageNumName ] = baseParam.page
            baseParam[ pageSizeName ] = baseParam.size
        
            if( pageNumName !== 'page' ){ delete baseParam.page }
            if( pageSizeName !== 'size' ){ delete baseParam.size }
            let param = {...baseParam, ..._this.model}
            this.pull(_this, api, {
                param,
                onEnd: () => {
                    if(opt.loading) wx.hideLoading()
                    _this.setData({ [pagingLoading]: false })
                    setTimeout(()=>{
                        _this.setData({ refreshLoading:false })
                    },500)
                    opt?.onEnd?.()
                },
                onError: () => {
                    _this[pageIndex + 'last'] = null
                },
                empty: opt.empty,
                setParam: opt.setParam,
                paging: true,
                auth: opt.auth,
                onNotLogin: opt?.onNoLogin ? opt?.onNoLogin :  () => {
                    $wx.login({ ok: () => opt?.auth ? _this.auth() : this.paging(_this, api, option) })
                },
                onTokenExpire: opt?.onTokenExpire ? opt?.onTokenExpire : () => {
                    $wx.login({ ok: () => opt?.auth ? _this.auth() : this.paging(_this, api, option) })
                },
            }).then( result => {
                let data = result[ dataName ] || result?.rows
                if(!Array.isArray(data)) data = []
                if(data.length > 0){
					if(opt.setData){ data = opt.setData( data ) }	// 二次处理数据
					if(opt.setName){ data = data.map( (v,i) => ({ ...v, ...opt.setName(v, i) }) ) } 		// 自定义数据字段
				}
                const page = +result[ pageNumName ]        // 当前页码
                const size = +result[ pageSizeName ]       // 每页多少条数据
                const pages = +result[ totalPagesName ]    // 总共多少条数据
                const total = +result[ totalItemsName ]    // 总共多少页
                const pager = { page , size,  total, pages, data, result }
                resolve({ data, pager, result })
                if (_this.data[dataName] === data) { return }
                if( !Array.isArray(_this.data[dataName]) ) _this.data[dataName] = []
                _this.setData({ 
                    [opt.pager]: pager,
                    [dataName]: refresh ? data : _this.data[dataName].concat(data)
                })
                _this[pageIndex] = +result[ pageNumName ] + 1
                // 数据混乱
                if(_this.data[dataName].length > total) console.error('操作频繁引起数据开始混乱')

                if ( total <= size ||  _this.data[dataName].length === total || page === total ) {
                    _this.setData({ [loadingComplete]: true })
                }else{
                    // 检测分页数据是否触底
                    setTimeout(()=>{
                        if(_this.data[dataName].length === 0) return
                        const hander = opt.cpt ? _this : wx
                        hander.createSelectorQuery().select(opt.id).boundingClientRect(rect => {
                            if($fn.hasObject(rect)){
                                if (rect.height <= winHeight) {
                                    _this[opt.paging]?.()
                                }
                            }
                        }).exec()
                    })
                }
            },data=>{
                reject(data)
            })
        })
    },
    abort(_this,name){
        const { length } = arguments
        if(length === 1){
            if($fn.hasObject(_this?.task)){
                for(let i in _this.task) _this.task?.[i]?.abort?.()
            }
        }else if(length === 2){
            _this.task[name]?.abort?.()
        }
    },
    serialize:serializeParam
}
export default Index